


#include "pokemod/skills/bug.inc"
#include "pokemod/skills/dragon.inc"
#include "pokemod/skills/electric.inc"
#include "pokemod/skills/fighting.inc"
#include "pokemod/skills/fire.inc"
#include "pokemod/skills/flying.inc"
#include "pokemod/skills/ghost.inc"
#include "pokemod/skills/grass.inc"
#include "pokemod/skills/ground.inc"
#include "pokemod/skills/ice.inc"
#include "pokemod/skills/normal.inc"
#include "pokemod/skills/poison.inc"
#include "pokemod/skills/psychic.inc"
#include "pokemod/skills/rock.inc"
#include "pokemod/skills/water.inc"

PM_FUNC_NORM_ATTRIB SkillInit()
{
	pokemod_funcstart_debug( "SkillInit" );

	SetupArray( g_iSkillNames, NAME_LEN );
	SetupArray( g_iSkillInfos, 1 );
	SetupTrie( g_iSkillTrie );

	for ( new POKESKILL:iSkillID; iSkillID < POKESKILL; iSkillID++ )
		SkillCall( iSkillID, CALL_REGISTER );

	pokemod_funcend_debug( "SkillInit" );
}

PM_FUNC_NORM_ATTRIB SkillUpdateCalls()
{
	pokemod_funcstart_debug( "SkillUpdateCalls (Pre=%d)", g_iSkillCalls );

	g_iSkillCalls = CALL_NONE;

	new Int:iPokeNum, Int:iSkillNum, POKESKILL:iSkillID;
	LoopThroughPlayersFlags( iPlayerID, PLAYER_FLAGS_NOHLTV )
	{
		iPokeNum = PlayerGetInfo( iPlayerID, PINFO_ACTIVE_POKENUM );
		for ( iSkillNum = 0; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
		{
			iSkillID = g_iPlayerPokemonSkills[ iPlayerID ][ iPokeNum ][ iSkillNum ];
			if ( iSkillID > SKILL_INVALID )
				g_iSkillCalls |= SKILL_CALL:SkillInfo( iSkillID, SI_CALLTYPE );
		}
	}

	pokemod_funcend_debug( "SkillUpdateCalls (Post=%d)", g_iSkillCalls );
}

PM_FUNC_NORM_ATTRIB SkillCallType( SKILL_CALL:iCall, Int:iPlayer, bool:bCheckReady = true )
{
	if ( !( g_iSkillCalls & iCall ) || iPlayer <= 0 || iPlayer > g_iLargestPlayer )
		return;

	SkillTimeCheckNull( iPlayer );

	// No using skills while asleep
	if ( g_iPlayerStatus[ iPlayer ][ STATUS_ASLEEP ][ STATUS_AMOUNT ] > 0 )
		return;

	pokemod_funcstart_debug( "SkillCallType" );

	new bool:bSkillReady = ( !g_fSkillReadyTime[ iPlayer ] );
	new bool:bPlayerAdmin = bool:is_user_admin( iPlayer );

	new Int:iPokeNum = PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM );
	new POKESKILL:iSkillID, SKILL_CALL:iSkillCalls;

	for ( new Int:iSkillNum; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
	{
		iSkillID = g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ iSkillNum ];
		if ( iSkillID <= SKILL_INVALID )
			continue;

		iSkillCalls = SKILL_CALL:SkillInfo( iSkillID, SI_CALLTYPE );

		if ( !( iSkillCalls & iCall ) )
			continue;

		if ( bCheckReady && !bSkillReady && !( iSkillCalls & CALL_WITH_SKILL_TIME ) )
			continue;

		if ( !bPlayerAdmin && ( iSkillCalls & CALL_ADMIN_ONLY ) )
			continue;

		SkillCall( .skillID = iSkillID, .call = iCall, .iPlayer = iPlayer, .skillNum = iSkillNum );
	}

	pokemod_funcend_debug( "SkillCallType" );
}

//Remember that this may not be called because of round end/changing pokemon
//You should have CALL_RESET to remove effects if they exist (use SkillDelayedCallExists)
//Effects to remove: persistant screen fades, lasting beams, etc.
PM_FUNC_NORM_ATTRIB SkillDelayCustomCall( Int:iPlayer, Float:fDelayTime, SKILL_CALL:iCall = CALL_CUSTOM )
{
	pokemod_funcstart_debug( "SkillDelayCustomCall" );

	new Int:iParm[ 4 ];
	iParm[ 0 ] = iPlayer;
	iParm[ 1 ] = _:g_iSkillID;
	iParm[ 2 ] = _:iCall;
	iParm[ 3 ] = g_iSkillNum;

	set_task( fDelayTime, "SkillDelayedCall", TASK_POKESKILL_CUSTOM + ( iPlayer * SKILLS_PER_POKEMON ) + g_iSkillNum, iParm, sizeof iParm );

	pokemod_funcend_debug( "SkillDelayCustomCall" );
}

PM_FUNC_STOCK_ATTRIB SkillDelayedCallExists( Int:iPlayer )
{
	return task_exists( TASK_POKESKILL_CUSTOM + ( iPlayer * SKILLS_PER_POKEMON ) + g_iSkillNum );
}

PM_FUNC_PUBLIC_ATTRIB SkillDelayedCall( Int:iParm[ 4 ] )
{
	pokemod_funcstart_debug( "SkillDelayedCall" );

	new Int:iPlayer = iParm[ 0 ];
	new POKESKILL:iSkillID = POKESKILL:iParm[ 1 ];
	new SKILL_CALL:iCall = SKILL_CALL:iParm[ 2 ];
	new Int:iSkillNum = iParm[ 3 ];

	SkillCall( iSkillID, iCall, iPlayer, iSkillNum );

	pokemod_funcend_debug( "SkillDelayedCall" );
}

PM_FUNC_NORM_ATTRIB SKILL:SkillCall( POKESKILL:skillID, SKILL_CALL:call, Int:iPlayer = 0, Int:skillNum = 0, bool:addDelayTime = true )
{
	new Int:iPokeNum;
	if ( iPlayer )
	{
		if ( !is_user_connected( iPlayer ) )
			return SKILL_DENY;

		iPokeNum = PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM );

		if ( call == CALL_BIND_DOWN )
		{
			if ( !g_iPlayerSkillPP[ iPlayer ][ iPokeNum ][ skillNum ] )
			{
				SoundDeny( iPlayer );
				client_print( iPlayer, print_chat, "[PokeMod] %s is out of PP. Find a PokeCenter to recharge.", SkillToName( skillID ) );
				return SKILL_DENY;
			}
			else if ( g_iRoundState & ROUND_FREEZETIME )
			{
				SoundDeny( iPlayer );
				client_print( iPlayer, print_chat, "[PokeMod] No skills allowed during freeze time." );
				return SKILL_DENY;
			}
			else
			{
				new SKILL_CALL:iCallTypes = SKILL_CALL:SkillInfo( skillID, SI_CALLTYPE );
				if ( !( iCallTypes & call ) )
				{
					client_print( iPlayer, print_chat, "[PokeMod] %s is used automatically.", SkillToName( skillID ) );
					SoundDeny( iPlayer );
					return SKILL_DENY;
				}
				else if ( g_fSkillReadyTime[ iPlayer ] && !( iCallTypes & CALL_WITH_SKILL_TIME ) )
				{
					SoundDeny( iPlayer );
					return SKILL_DENY;
				}
			}
		}
		else
		{
			if ( !g_iPlayerSkillPP[ iPlayer ][ iPokeNum ][ skillNum ] )
			{
				return SKILL_DENY;
			}
		}
	}

	pokemod_funcstart_debug( "SkillCall" );

	g_iSkillID = skillID;
	g_iSkillNum = skillNum;

	new SKILL:iSkillReturn;

	switch( skillID )
	{
		case SKILL_ABSORB:               iSkillReturn = SkillAbsorb( call, iPlayer );
		case SKILL_ACID:                 iSkillReturn = SkillAcid( call, iPlayer );
		case SKILL_ACIDARMOR:            iSkillReturn = SkillAcidArmor( call, iPlayer );
		case SKILL_AGILITY:              iSkillReturn = SkillAgility( call, iPlayer );
		case SKILL_AMNESIA:              iSkillReturn = SkillAmnesia( call, iPlayer );
		case SKILL_AURORABEAM:           iSkillReturn = SkillAuroraBeam( call, iPlayer );
		case SKILL_BARRAGE:              iSkillReturn = SkillBarrage( call, iPlayer );
		case SKILL_BARRIER:              iSkillReturn = SkillBarrier( call, iPlayer );
		case SKILL_BIDE:                 iSkillReturn = SkillBide( call, iPlayer );
		case SKILL_BIND:                 iSkillReturn = SkillBind( call, iPlayer );
		case SKILL_BITE:                 iSkillReturn = SkillBite( call, iPlayer );
		case SKILL_BLIZZARD:             iSkillReturn = SkillBlizzard( call, iPlayer );
		case SKILL_BODYSLAM:             iSkillReturn = SkillBodySlam( call, iPlayer );
		case SKILL_BONECLUB:             iSkillReturn = SkillBoneClub( call, iPlayer );
		case SKILL_BONEMERANG:           iSkillReturn = SkillBonemerang( call, iPlayer );
		case SKILL_BUBBLE:               iSkillReturn = SkillBubble( call, iPlayer );
		case SKILL_BUBBLEBEAM:           iSkillReturn = SkillBubblebeam( call, iPlayer );
		case SKILL_CLAMP:                iSkillReturn = SkillClamp( call, iPlayer );
		case SKILL_COMETPUNCH:           iSkillReturn = SkillCometPunch( call, iPlayer );
		case SKILL_CONFUSERAY:           iSkillReturn = SkillConfuseRay( call, iPlayer );
		case SKILL_CONFUSION:            iSkillReturn = SkillConfusion( call, iPlayer );
		case SKILL_CONSTRICT:            iSkillReturn = SkillConstrict( call, iPlayer );
		case SKILL_CONVERSION:           iSkillReturn = SkillConversion( call, iPlayer );
		case SKILL_COUNTER:              iSkillReturn = SkillCounter( call, iPlayer );
		case SKILL_CRABHAMMER:           iSkillReturn = SkillCrabhammer( call, iPlayer );
		case SKILL_CUT:                  iSkillReturn = SkillCut( call, iPlayer );
		case SKILL_DEFENSECURL:          iSkillReturn = SkillDefenseCurl( call, iPlayer );
		case SKILL_DIG:                  iSkillReturn = SkillDig( call, iPlayer );
		case SKILL_DISABLE:              iSkillReturn = SkillDisable( call, iPlayer );
		case SKILL_DIZZYPUNCH:           iSkillReturn = SkillDizzyPunch( call, iPlayer );
		case SKILL_DOUBLEKICK:           iSkillReturn = SkillDoubleKick( call, iPlayer );
		case SKILL_DOUBLETEAM:           iSkillReturn = SkillDoubleTeam( call, iPlayer );
		case SKILL_DOUBLEEDGE:           iSkillReturn = SkillDoubleEdge( call, iPlayer );
		case SKILL_DOUBLESLAP:           iSkillReturn = SkillDoubleslap( call, iPlayer );
		case SKILL_DRAGONRAGE:           iSkillReturn = SkillDragonRage( call, iPlayer );
		case SKILL_DREAMEATER:           iSkillReturn = SkillDreamEater( call, iPlayer );
		case SKILL_DRILLPECK:            iSkillReturn = SkillDrillPeck( call, iPlayer );
		case SKILL_EARTHQUAKE:           iSkillReturn = SkillEarthquake( call, iPlayer );
		case SKILL_EGGBOMB:              iSkillReturn = SkillEggBomb( call, iPlayer );
		case SKILL_EMBER:                iSkillReturn = SkillEmber( call, iPlayer );
		case SKILL_EXPLOSION:            iSkillReturn = SkillExplosion( call, iPlayer );
		case SKILL_FIREBLAST:            iSkillReturn = SkillFireBlast( call, iPlayer );
		case SKILL_FIREPUNCH:            iSkillReturn = SkillFirePunch( call, iPlayer );
		case SKILL_FIRESPIN:             iSkillReturn = SkillFireSpin( call, iPlayer );
		case SKILL_FISSURE:              iSkillReturn = SkillFissure( call, iPlayer );
		case SKILL_FLAMETHROWER:         iSkillReturn = SkillFlamethrower( call, iPlayer );
		case SKILL_FLASH:                iSkillReturn = SkillFlash( call, iPlayer );
		case SKILL_FLY:                  iSkillReturn = SkillFly( call, iPlayer );
		case SKILL_FOCUSENERGY:          iSkillReturn = SkillFocusEnergy( call, iPlayer );
		case SKILL_FURYATTACK:           iSkillReturn = SkillFuryAttack( call, iPlayer );
		case SKILL_FURYSWIPES:           iSkillReturn = SkillFurySwipes( call, iPlayer );
		case SKILL_GLARE:                iSkillReturn = SkillGlare( call, iPlayer );
		case SKILL_GROWL:                iSkillReturn = SkillGrowl( call, iPlayer );
		case SKILL_GROWTH:               iSkillReturn = SkillGrowth( call, iPlayer );
		case SKILL_GUILLOTINE:           iSkillReturn = SkillGuillotine( call, iPlayer );
		case SKILL_GUST:                 iSkillReturn = SkillGust( call, iPlayer );
		case SKILL_HARDEN:               iSkillReturn = SkillHarden( call, iPlayer );
		case SKILL_HAZE:                 iSkillReturn = SkillHaze( call, iPlayer );
		case SKILL_HEADBUTT:             iSkillReturn = SkillHeadButt( call, iPlayer );
		case SKILL_HIJUMPKICK:           iSkillReturn = SkillHiJumpKick( call, iPlayer );
		case SKILL_HORNATTACK:           iSkillReturn = SkillHornAttack( call, iPlayer );
		case SKILL_HORNDRILL:            iSkillReturn = SkillHornDrill( call, iPlayer );
		case SKILL_HYDROPUMP:            iSkillReturn = SkillHydroPump( call, iPlayer );
		case SKILL_HYPERBEAM:            iSkillReturn = SkillHyperBeam( call, iPlayer );
		case SKILL_HYPERFANG:            iSkillReturn = SkillHyperFang( call, iPlayer );
		case SKILL_HYPNOSIS:             iSkillReturn = SkillHypnosis( call, iPlayer );
		case SKILL_ICEBEAM:              iSkillReturn = SkillIceBeam( call, iPlayer );
		case SKILL_ICEPUNCH:             iSkillReturn = SkillIcePunch( call, iPlayer );
		case SKILL_JUMPKICK:             iSkillReturn = SkillJumpKick( call, iPlayer );
		case SKILL_KARATECHOP:           iSkillReturn = SkillKarateChop( call, iPlayer );
		case SKILL_KINESIS:              iSkillReturn = SkillKinesis( call, iPlayer );
		case SKILL_LEECHLIFE:            iSkillReturn = SkillLeechLife( call, iPlayer );
		case SKILL_LEECHSEED:            iSkillReturn = SkillLeechSeed( call, iPlayer );
		case SKILL_LEER:                 iSkillReturn = SkillLeer( call, iPlayer );
		case SKILL_LICK:                 iSkillReturn = SkillLick( call, iPlayer );
		case SKILL_LIGHTSCREEN:          iSkillReturn = SkillLightScreen( call, iPlayer );
		case SKILL_LOVELYKISS:           iSkillReturn = SkillLovelyKiss( call, iPlayer );
		case SKILL_LOWKICK:              iSkillReturn = SkillLowKick( call, iPlayer );
		case SKILL_MEDITATE:             iSkillReturn = SkillMeditate( call, iPlayer );
		case SKILL_MEGADRAIN:            iSkillReturn = SkillMegaDrain( call, iPlayer );
		case SKILL_MEGAKICK:             iSkillReturn = SkillMegaKick( call, iPlayer );
		case SKILL_MEGAPUNCH:            iSkillReturn = SkillMegaPunch( call, iPlayer );
		case SKILL_METRONOME:            iSkillReturn = SkillMetronome( call, iPlayer );
		case SKILL_MIMIC:                iSkillReturn = SkillMimic( call, iPlayer );
		case SKILL_MINIMIZE:             iSkillReturn = SkillMinimize( call, iPlayer );
		case SKILL_MIRRORMOVE:           iSkillReturn = SkillMirrorMove( call, iPlayer );
		case SKILL_MIST:                 iSkillReturn = SkillMist( call, iPlayer );
		case SKILL_NIGHTSHADE:           iSkillReturn = SkillNightShade( call, iPlayer );
		case SKILL_PAYDAY:               iSkillReturn = SkillPayDay( call, iPlayer );
		case SKILL_PECK:                 iSkillReturn = SkillPeck( call, iPlayer );
		case SKILL_PETALDANCE:           iSkillReturn = SkillPetalDance( call, iPlayer );
		case SKILL_PINMISSILE:           iSkillReturn = SkillPinMissile( call, iPlayer );
		case SKILL_POISONGAS:            iSkillReturn = SkillPoisonGas( call, iPlayer );
		case SKILL_POISONSTING:          iSkillReturn = SkillPoisonSting( call, iPlayer );
		case SKILL_POISONPOWDER:         iSkillReturn = SkillPoisonPowder( call, iPlayer );
		case SKILL_POUND:                iSkillReturn = SkillPound( call, iPlayer );
		case SKILL_PSYBEAM:              iSkillReturn = SkillPsybeam( call, iPlayer );
		case SKILL_PSYCHIC:              iSkillReturn = SkillPsychic( call, iPlayer );
		case SKILL_PSYWAVE:              iSkillReturn = SkillPsywave( call, iPlayer );
		case SKILL_QUICKATTACK:          iSkillReturn = SkillQuickAttack( call, iPlayer );
		case SKILL_RAGE:                 iSkillReturn = SkillRage( call, iPlayer );
		case SKILL_RAZORLEAF:            iSkillReturn = SkillRazorLeaf( call, iPlayer );
		case SKILL_RAZORWIND:            iSkillReturn = SkillRazorWind( call, iPlayer );
		case SKILL_RECOVER:              iSkillReturn = SkillRecover( call, iPlayer );
		case SKILL_REFLECT:              iSkillReturn = SkillReflect( call, iPlayer );
		case SKILL_REST:                 iSkillReturn = SkillRest( call, iPlayer );
		case SKILL_ROAR:                 iSkillReturn = SkillRoar( call, iPlayer );
		case SKILL_ROCKSLIDE:            iSkillReturn = SkillRockSlide( call, iPlayer );
		case SKILL_ROCKTHROW:            iSkillReturn = SkillRockThrow( call, iPlayer );
		case SKILL_ROLLINGKICK:          iSkillReturn = SkillRollingKick( call, iPlayer );
		case SKILL_SANDATTACK:           iSkillReturn = SkillSandAttack( call, iPlayer );
		case SKILL_SCRATCH:              iSkillReturn = SkillScratch( call, iPlayer );
		case SKILL_SCREECH:              iSkillReturn = SkillScreech( call, iPlayer );
		case SKILL_SEISMICTOSS:          iSkillReturn = SkillSeismicToss( call, iPlayer );
		case SKILL_SELFDESTRUCT:         iSkillReturn = SkillSelfDestruct( call, iPlayer );
		case SKILL_SHARPEN:              iSkillReturn = SkillSharpen( call, iPlayer );
		case SKILL_SING:                 iSkillReturn = SkillSing( call, iPlayer );
		case SKILL_SKULLBASH:            iSkillReturn = SkillSkullBash( call, iPlayer );
		case SKILL_SKYATTACK:            iSkillReturn = SkillSkyAttack( call, iPlayer );
		case SKILL_SLAM:                 iSkillReturn = SkillSlam( call, iPlayer );
		case SKILL_SLASH:                iSkillReturn = SkillSlash( call, iPlayer );
		case SKILL_SLEEPPOWDER:          iSkillReturn = SkillSleepPowder( call, iPlayer );
		case SKILL_SLUDGE:               iSkillReturn = SkillSludge( call, iPlayer );
		case SKILL_SMOG:                 iSkillReturn = SkillSmog( call, iPlayer );
		case SKILL_SMOKESCREEN:          iSkillReturn = SkillSmokescreen( call, iPlayer );
		case SKILL_SOFTBOILED:           iSkillReturn = SkillSoftboiled( call, iPlayer );
		case SKILL_SOLARBEAM:            iSkillReturn = SkillSolarBeam( call, iPlayer );
		case SKILL_SONICBOOM:            iSkillReturn = SkillSonicboom( call, iPlayer );
		case SKILL_SPIKECANNON:          iSkillReturn = SkillSpikeCannon( call, iPlayer );
		case SKILL_SPLASH:               iSkillReturn = SkillSplash( call, iPlayer );
		case SKILL_SPORE:                iSkillReturn = SkillSpore( call, iPlayer );
		case SKILL_STOMP:                iSkillReturn = SkillStomp( call, iPlayer );
		case SKILL_STRENGTH:             iSkillReturn = SkillStrength( call, iPlayer );
		case SKILL_STRINGSHOT:           iSkillReturn = SkillStringShot( call, iPlayer );
		case SKILL_STRUGGLE:             iSkillReturn = SkillStruggle( call, iPlayer );
		case SKILL_STUNSPORE:            iSkillReturn = SkillStunSpore( call, iPlayer );
		case SKILL_SUBMISSION:           iSkillReturn = SkillSubmission( call, iPlayer );
		case SKILL_SUBSTITUTE:           iSkillReturn = SkillSubstitute( call, iPlayer );
		case SKILL_SUPERFANG:            iSkillReturn = SkillSuperFang( call, iPlayer );
		case SKILL_SUPERSONIC:           iSkillReturn = SkillSupersonic( call, iPlayer );
		case SKILL_SURF:                 iSkillReturn = SkillSurf( call, iPlayer );
		case SKILL_SWIFT:                iSkillReturn = SkillSwift( call, iPlayer );
		case SKILL_SWORDSDANCE:          iSkillReturn = SkillSwordsDance( call, iPlayer );
		case SKILL_TACKLE:               iSkillReturn = SkillTackle( call, iPlayer );
		case SKILL_TAILWHIP:             iSkillReturn = SkillTailWhip( call, iPlayer );
		case SKILL_TAKEDOWN:             iSkillReturn = SkillTakeDown( call, iPlayer );
		case SKILL_TELEPORT:             iSkillReturn = SkillTeleport( call, iPlayer );
		case SKILL_THRASH:               iSkillReturn = SkillThrash( call, iPlayer );
		case SKILL_THUNDER:              iSkillReturn = SkillThunder( call, iPlayer );
		case SKILL_THUNDERWAVE:          iSkillReturn = SkillThunderWave( call, iPlayer );
		case SKILL_THUNDERBOLT:          iSkillReturn = SkillThunderBolt( call, iPlayer );
		case SKILL_THUNDERPUNCH:         iSkillReturn = SkillThunderPunch( call, iPlayer );
		case SKILL_THUNDERSHOCK:         iSkillReturn = SkillThunderShock( call, iPlayer );
		case SKILL_TOXIC:                iSkillReturn = SkillToxic( call, iPlayer );
		case SKILL_TRANSFORM:            iSkillReturn = SkillTransform( call, iPlayer );
		case SKILL_TRIATTACK:            iSkillReturn = SkillTriAttack( call, iPlayer );
		case SKILL_TWINNEEDLE:           iSkillReturn = SkillTwinNeedle( call, iPlayer );
		case SKILL_VICEGRIP:             iSkillReturn = SkillViceGrip( call, iPlayer );
		case SKILL_VINEWHIP:             iSkillReturn = SkillVineWhip( call, iPlayer );
		case SKILL_WATERGUN:             iSkillReturn = SkillWaterGun( call, iPlayer );
		case SKILL_WATERFALL:            iSkillReturn = SkillWaterfall( call, iPlayer );
		case SKILL_WHIRLWIND:            iSkillReturn = SkillWhirlwind( call, iPlayer );
		case SKILL_WINGATTACK:           iSkillReturn = SkillWingAttack( call, iPlayer );
		case SKILL_WITHDRAW:             iSkillReturn = SkillWithdraw( call, iPlayer );
		case SKILL_WRAP:                 iSkillReturn = SkillWrap( call, iPlayer );

		default:                         pokemod_debug( "Skill %d not found", skillID );
	}

	g_iSkillID = SKILL_INVALID;

	if ( iPlayer )
	{
		switch ( iSkillReturn )
		{
			case SKILL_USED, SKILL_USED_PAUSE:
			{
				if ( addDelayTime )
				{
					g_iPlayerSkillCharges[ iPlayer ][ skillNum ]++;
					if ( g_iPlayerSkillCharges[ iPlayer ][ skillNum ] >= SkillInfo( skillID, SI_CHARGES ) )
					{
						g_iPlayerSkillCharges[ iPlayer ][ skillNum ] = 0;

						new Int:iPP = ( --g_iPlayerSkillPP[ iPlayer ][ iPokeNum ][ skillNum ] );

						//Save PP every 5 uses
						if ( !( iPP % 5 ) )
						{
							SavePlayerPokeSkill( iPlayer, iPokeNum, skillNum );
						}

						new Float:fDelayTime = Float:SkillInfo( skillID, SI_DELAYTIME );
						if ( fDelayTime )
						{
							SkillAddDelay( iPlayer, fDelayTime );

							if ( is_user_alive( iPlayer ) )
							{
								SkillCallType( CALL_RESET, iPlayer, .bCheckReady = false );
							}
						}
					}

					if ( call == CALL_BIND_DOWN )
					{
						SkillMenu( iPlayer );
					}

					PlayerHUD( iPlayer, iPlayer );
				}
				else if ( g_fSkillReadyTime[ iPlayer ] == SKILL_TIME_WAIT )
				{
					SkillSetDelay( iPlayer, 0.0 );
				}
			}
			case SKILL_WAIT:
			{
				SkillSetDelay( iPlayer, SKILL_TIME_WAIT );
			}
			case SKILL_DENY:
			{
				SoundDeny( iPlayer );
			}
		}
	}

	pokemod_funcend_debug( "SkillCall" );

	return iSkillReturn;
}
PM_FUNC_NORM_ATTRIB SKILL:SkillRegister( String:Name[], PM_TYPE:Type, SKILL_CALL:callTypes, Int:PP, Int:Charges=1, Float:DelayTime = 0.0 )
{
	pokemod_funcstart_debug( "SkillRegister %s", Name );

	TrieSetCell( g_iSkillTrie, Name, ArraySize( g_iSkillNames ) );
	ArrayPushString( g_iSkillNames, Name );

	new Int:skillInfos[ SKILL_INFO ];
	skillInfos[ SI_TYPE ] = _:Type;
	skillInfos[ SI_CALLTYPE ] = _:callTypes;
	skillInfos[ SI_PP ] = clamp( PP, 1, 100 );
	skillInfos[ SI_CHARGES ] = Charges;
	skillInfos[ SI_DELAYTIME ] = _:DelayTime;

	for ( new SKILL_INFO:i; i < SKILL_INFO; i++ )
	{
		ArrayPushCell( g_iSkillInfos, skillInfos[ i ] );
	}

	pokemod_funcend_debug( "SkillRegister" );

	return SKILL_READY;
}
PM_FUNC_NORM_ATTRIB SkillInfo( POKESKILL:iSkillID, SKILL_INFO:iInfoNum )
{
	if ( iSkillID <= SKILL_INVALID )
		return 0;
	return ArrayGetCell( g_iSkillInfos, _:( ( _:iSkillID * _:SKILL_INFO ) + _:iInfoNum ) );
}
PM_FUNC_NORM_ATTRIB SKILL:SkillHelp( String:szHelp[] )
{
	copy( g_szSkillHelp, charsmax(g_szSkillHelp), szHelp );
	return SKILL_READY;
}
PM_FUNC_NORM_ATTRIB SkillGetHelp( POKESKILL:iSkillID )
{
	SkillCall( iSkillID, CALL_HELP );
	return g_szSkillHelp;
}

PM_FUNC_NORM_ATTRIB SkillMenu( Int:iPlayer )
{
	new Int:iPokeNum = PlayerGetInfo( iPlayer, PINFO_ACTIVE_POKENUM );

	//If they don't have a skill
	if ( g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ 0 ] == SKILL_INVALID )
		return false;

	if ( g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_HP ] <= 0 )
	{
		client_print( iPlayer, print_chat, "[PokeMod] Fainted pokemon cannot use skills." );
		return false;
	}

	pokemod_funcstart_debug( "SkillMenu" );

	new Int:iActiveSkillNum = PlayerGetInfo( iPlayer, PINFO_ACTIVE_SKILLNUM ) - 1;

	new Int:iMenu = MenuCreate( "%s Skills", PokeNumToName( iPokeNum, .playerID = iPlayer ) );

	for ( new Int:cColor, POKESKILL:iSkillID, Int:iSkillNum, String:szSkillNum[ 16 ], Int:iSkillMaxCharges, Int:iSkillMaxPPs;
		iSkillNum < SKILLS_PER_POKEMON;
		iSkillNum++
	){
		iSkillID = g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ iSkillNum ];
		if ( iSkillID != SKILL_INVALID )
		{
			if ( iActiveSkillNum == iSkillNum )
				cColor = 'r';
			else if ( !( SKILL_CALL:SkillInfo( iSkillID, SI_CALLTYPE ) & ( CALL_BIND_DOWN | CALL_BIND_UP ) ) )
				cColor = 'd';
			else
				cColor = 'w';

			iSkillMaxPPs = SkillInfo( iSkillID, SI_PP );
			iSkillMaxCharges = SkillInfo( iSkillID, SI_CHARGES );
			num_to_str( iSkillNum + 1, szSkillNum, charsmax(szSkillNum) );

			if ( iSkillMaxCharges > 1 && g_iPlayerSkillCharges[ iPlayer ][ iSkillNum ] )
			{
				MenuAddItem( iMenu, szSkillNum, "\%c(%02d/%02d PP) %s (%d%%)",
											cColor,
											g_iPlayerSkillPP[ iPlayer ][ iPokeNum ][ iSkillNum ],
											iSkillMaxPPs,
											SkillToName( iSkillID ),
											( 100 * g_iPlayerSkillCharges[ iPlayer ][ iSkillNum ] ) / ( iSkillMaxCharges )
											);
			}
			else
			{
				MenuAddItem( iMenu, szSkillNum, "\%c(%02d/%02d PP) %s",
											cColor,
											g_iPlayerSkillPP[ iPlayer ][ iPokeNum ][ iSkillNum ],
											iSkillMaxPPs,
											SkillToName( iSkillID )
											);
			}
		}
	}

	MenuExit( iPlayer, iMenu, .bExitOption = true );
	MenuDisplay( iPlayer, iMenu, MENU_SKILLS );

	pokemod_funcend_debug( "SkillMenu" );

	return true;
}

PM_FUNC_NORM_ATTRIB SkillLearn( Int:iPlayer, Int:iPokeNum, POKESKILL:iSkillID )
{
	if ( iSkillID == SKILL_INVALID || !g_iPlayerPokemonStats[ iPlayer ][ iPokeNum ][ POKESTAT_POKEMON ] || PlayerHasSkill( iPlayer, iPokeNum, iSkillID ) != -1 )
		return -1;

	pokemod_funcstart_debug( "SkillLearn" );

	for ( new Int:iSkillNum; iSkillNum < SKILLS_PER_POKEMON; iSkillNum++ )
	{
		if ( g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ iSkillNum ] == SKILL_INVALID )
		{
			g_iPlayerPokemonSkills[ iPlayer ][ iPokeNum ][ iSkillNum ] = iSkillID;
			g_iPlayerSkillPP[ iPlayer ][ iPokeNum ][ iSkillNum ] = SkillInfo( iSkillID, SI_PP );
			SavePlayerPokeSkill( iPlayer, iPokeNum, iSkillNum );

			DHudEventMessage( iPlayer, "%s learned %s.", PokeNumToName( iPokeNum, .playerID = iPlayer ), SkillToName( iSkillID ) );

			pokemod_funcend_debug( "SkillLearn Auto" );
			return iSkillNum;
		}
	}

	new Int:menu = MenuCreate( "%s is trying to learn %s", PokeNumToName( iPokeNum, .playerID = iPlayer ), SkillToName( iSkillID ) );

	new String:szData[ 32 ];
	formatex( szData, charsmax(szData), "%d %d -1", iPokeNum, iSkillID );

	MenuAddItem( menu, szData, "Learn skill %s", SkillToName( iSkillID ) );

	MenuAddItem( menu, MENU_FAKE_EXIT, "Block Skill^n^n%s", SkillGetHelp( iSkillID ) );

	MenuExit( iPlayer, menu, .bExitOption = false );
	MenuDisplay( iPlayer, menu, MENU_LEARN_SKILL );

	pokemod_funcend_debug( "SkillLearn Menu" );

	return -1;
}

/*
	SkillDamage( .victim = ,
				.attacker = ,
				.damage =
				);
*/
PM_FUNC_NORM_ATTRIB SkillDamage( Int:victim, Int:attacker, Int:damage )
{
	pokemod_funcstart_debug( "SkillDamage" );

	new POKESKILL:iSkillID = g_iSkillID;
	if ( iSkillID <= SKILL_INVALID )
	{
		pokemod_debug( "SkillDamage called with invalid skill ID" );
		return 0;
	}

	new String:szSkillName[ NAME_LEN ];
	SkillToName( iSkillID, szSkillName, charsmax(szSkillName) );

	new Int:iDamageDone = DamageFake( victim, attacker, damage, szSkillName, PM_TYPE:SkillInfo( iSkillID, SI_TYPE ), PlayerType( victim ) );

	if ( PlayerGetInfo( attacker, PINFO_SETTING_SHOWDAMAGE ) )
	{
		if ( iDamageDone > 0 )
		{
			DHudEventMessage( attacker, "%s did %d damage.", szSkillName, iDamageDone );
		}
		else if ( iDamageDone < 0 )
		{
			DHudEventMessage( attacker, "%s healed %d HP.", szSkillName, abs(iDamageDone) );
		}
	}

	pokemod_funcend_debug( "SkillDamage" );

	return iDamageDone;
}

/*
	SkillAoEDamage( .attacker,
					.cOrigin = ,
					.fRadius = ,
					.iMaxDamage = ,
					.flags = AOE_NORMAL
					);
*/
PM_FUNC_NORM_ATTRIB SkillAoEDamage( Int:attacker, Coord:cOrigin[3], Float:fRadius, Int:iMaxDamage, AOE_FLAGS:flags=AOE_NORMAL )
{
	pokemod_funcstart_debug( "SkillAoEDamage" );

	new POKESKILL:iSkillID = g_iSkillID;
	if ( iSkillID <= SKILL_INVALID )
	{
		pokemod_debug( "SkillAoEDamage called with invalid skill ID" );

		pokemod_funcend_debug( "SkillAoEDamage Invalid Skill" );

		return 0;
	}

	new PM_TYPE:skillType = PM_TYPE:SkillInfo( iSkillID, SI_TYPE );
	new String:szSkillName[ NAME_LEN ];
	SkillToName( iSkillID, szSkillName, charsmax(szSkillName) );

	new Coord:cPlayerOrigin[3], Int:iDistance, Int:damage, Int:totalDamage;

	new CsTeams:attackerTeam = cs_get_user_team( attacker );

	LoopThroughPlayersFlags( iPlayer, PLAYER_FLAGS_ALIVE )
	{
		if ( attacker == iPlayer && flags & AOE_IGNORE_ATTACKER )
			continue;

		if ( flags & ( AOE_VICTIM_TEAM | AOE_ATTACKER_TEAM ) )
		{
			if ( attackerTeam == cs_get_user_team( iPlayer ) )
			{
				if ( flags & AOE_VICTIM_TEAM )
					continue;
			}
			else
			{
				if ( flags & AOE_ATTACKER_TEAM )
					continue;
			}
		}

		get_user_origin( iPlayer, cPlayerOrigin );

		iDistance = get_distance( cPlayerOrigin, cOrigin );

		if ( 0.0 <= iDistance <= fRadius )
		{
			if ( flags & AOE_MAXIMIZE_DAMAGE )
				damage = iMaxDamage;
			else
				damage = iMaxDamage - floatround( ( iMaxDamage * iDistance ) / fRadius );

			totalDamage += DamageFake( iPlayer, attacker, damage, szSkillName, skillType, PlayerType( iPlayer ) );
		}
	}

	if ( PlayerGetInfo( attacker, PINFO_SETTING_SHOWDAMAGE ) )
	{
		if ( totalDamage > 0 )
		{
			DHudEventMessage( attacker, "%s did %d damage.", szSkillName, totalDamage );
		}
		else if ( totalDamage < 0 )
		{
			DHudEventMessage( attacker, "%s healed %d HP.", szSkillName, abs(totalDamage) );
		}
	}

	pokemod_funcend_debug( "SkillAoEDamage" );

	return totalDamage;
}

/*
	SkillLineDamage( .attacker = ,
					.cStart = ,
					.cEnd = ,
					.fRadius = ,
					.iMaxDamage = ,
					.flags = AOE_IGNORE_ATTACKER | AOE_VICTIM_TEAM
					);
*/
PM_FUNC_NORM_ATTRIB SkillLineDamage( Int:attacker, Coord:cStart[3], Coord:cEnd[3], Float:fRadius, Int:iMaxDamage, AOE_FLAGS:flags=AOE_IGNORE_ATTACKER|AOE_VICTIM_TEAM )
{
	pokemod_funcstart_debug( "SkillLineDamage" );

	new POKESKILL:iSkillID = g_iSkillID;
	if ( iSkillID <= SKILL_INVALID )
	{
		pokemod_debug( "SkillLineDamage called with invalid skill ID" );
		return 0;
	}

	new PM_TYPE:skillType = PM_TYPE:SkillInfo( iSkillID, SI_TYPE );
	new String:szSkillName[ NAME_LEN ];
	SkillToName( iSkillID, szSkillName, charsmax(szSkillName) );

	new Vector:vStart[3], Vector:vEnd[3];
	CoordToVector( cStart, vStart );
	CoordToVector( cEnd, vEnd );
	new Vector:vPlayerOrigin[3], Int:iDistance, Int:damage, Int:totalDamage;

	new CsTeams:attackerTeam = cs_get_user_team( attacker );

	LoopThroughPlayersFlags( iPlayer, PLAYER_FLAGS_ALIVE )
	{
		if ( attacker == iPlayer && flags & AOE_IGNORE_ATTACKER )
			continue;

		if ( flags & ( AOE_VICTIM_TEAM | AOE_ATTACKER_TEAM ) )
		{
			if ( attackerTeam == cs_get_user_team( iPlayer ) )
			{
				if ( flags & AOE_VICTIM_TEAM )
					continue;
			}
			else
			{
				if ( flags & AOE_ATTACKER_TEAM )
					continue;
			}
		}

		EntityGetOrigin( iPlayer, vPlayerOrigin );

		iDistance = get_distance_to_line( vStart, vEnd, vPlayerOrigin );

		if ( 0.0 <= iDistance <= fRadius )
		{
			if ( flags & AOE_MAXIMIZE_DAMAGE )
				damage = iMaxDamage;
			else
				damage = iMaxDamage - floatround( ( iMaxDamage * iDistance ) / fRadius );

			totalDamage += DamageFake( iPlayer, attacker, damage, szSkillName, skillType, PlayerType( iPlayer ) );
		}
	}

	pokemod_funcend_debug( "SkillLineDamage" );

	return totalDamage;
}

PM_FUNC_NORM_ATTRIB SkillTimeCheckNull( Int:iPlayer )
{
	pokemod_funcstart_debug( "SkillTimeCheckNull" );

	if ( g_fSkillReadyTime[ iPlayer ] > 0.0 && g_fSkillReadyTime[ iPlayer ] - get_gametime() <= 0.0 )
		g_fSkillReadyTime[ iPlayer ] = 0.0;

	pokemod_funcend_debug( "SkillTimeCheckNull" );
}

PM_FUNC_NORM_ATTRIB SkillAddDelay( Int:iPlayer, Float:fDelayTime )
{
	pokemod_funcstart_debug( "SkillAddDelay" );

	//If they currently are waiting for the skill, we need to null their wait time
	if ( g_fSkillReadyTime[ iPlayer ] == SKILL_TIME_WAIT )
		g_fSkillReadyTime[ iPlayer ] = 0.0;

	//If they don't have any delay time currently, we need to add the current game time
	if ( !g_fSkillReadyTime[ iPlayer ] )
		fDelayTime += get_gametime();

	g_fSkillReadyTime[ iPlayer ] += fDelayTime;

	pokemod_funcend_debug( "SkillAddDelay" );
}

PM_FUNC_NORM_ATTRIB SkillSetDelay( Int:iPlayer, Float:fDelayTime )
{
	pokemod_funcstart_debug( "SkillSetDelay" );

	g_fSkillReadyTime[ iPlayer ] = fDelayTime;

	pokemod_funcend_debug( "SkillSetDelay" );
}

/*
	SkillBoost( .iPlayer = iPlayer,
				.fVerticalBoost = StatClamp( iPlayer, STAT_SPECIAL, 200.0, 400.0 ),
				.fHorizontalBoost = StatClamp( iPlayer, STAT_SPECIAL, 400.0, 700.0 ),
				.iBoostDamage = StatClamp( iPlayer, STAT_ATTACK, 40, 70 )
				);
*/
PM_FUNC_NORM_ATTRIB SkillBoost( Int:iPlayer, Float:fVerticalBoost, Float:fHorizontalBoost, Int:iBoostDamage = 0 )
{
	pokemod_funcstart_debug( "SkillBoost" );

	new Vector:vVelocity[3];
	velocity_by_aim( iPlayer, 1, vVelocity );

	new Float:fHorLen = floatsqroot( vVelocity[0]*vVelocity[0] + vVelocity[1]*vVelocity[1] );
	vVelocity[0] *= fHorizontalBoost / fHorLen;
	vVelocity[1] *= fHorizontalBoost / fHorLen;
	vVelocity[2] = floatmax( fVerticalBoost, 200.0 );

	EntitySetVelocity( iPlayer, vVelocity );

	PlayerSetInfo( iPlayer, PINFO_BOOST_SKILL, _:g_iSkillID );
	PlayerSetInfo( iPlayer, PINFO_BOOST_SKILLNUM, g_iSkillNum );
	PlayerSetInfo( iPlayer, PINFO_BOOST_DAMAGE, iBoostDamage );

	pokemod_funcend_debug( "SkillBoost" );
}

/*
	CHECK_SKY_AIM
	CHECK_BOMB
	CHECK_MOVING
	CHECK_ON_GROUND
	CHECK_IN_AIR
	CHECK_IN_WATER

	SkillCheck( iPlayer, iCheckTypes, "" )
*/
PM_FUNC_NORM_ATTRIB SkillCheck( Int:iPlayer, CHECK_TYPES:iCheckTypes, String:szAction[] = "" )
{
	new bool:bShowMessage = ( szAction[0] == EOS ) ? false : true;
	if ( iCheckTypes & CHECK_SKY_AIM )
	{
		if ( fm_ViewContents( iPlayer ) == CONTENTS_SKY )
		{
			if ( bShowMessage ) client_print( iPlayer, print_center, "Unable to %s while looking at the sky", szAction );
			return true;
		}
	}
	if ( iCheckTypes & CHECK_BOMB )
	{
		if ( get_user_weapon( iPlayer ) == CSW_C4 )
		{
			if ( bShowMessage ) client_print( iPlayer, print_center, "Unable to %s while holding the bomb", szAction );
			return true;
		}
	}
	if ( iCheckTypes & CHECK_ON_GROUND )
	{
		if ( entity_on_ground( iPlayer ) )
		{
			if ( bShowMessage ) client_print( iPlayer, print_center, "Unable to %s while on the ground", szAction );
			return true;
		}
	}
	if ( iCheckTypes & CHECK_IN_AIR )
	{
		if ( !entity_on_ground( iPlayer ) )
		{
			if ( bShowMessage ) client_print( iPlayer, print_center, "Unable to %s while in the air", szAction );
			return true;
		}
	}
	if ( iCheckTypes & CHECK_IN_WATER )
	{
		if ( entity_in_water( iPlayer ) )
		{
			if ( bShowMessage ) client_print( iPlayer, print_center, "Unable to %s while in water", szAction );
			return true;
		}
	}
	if ( iCheckTypes & CHECK_MOVING )
	{
		new Vector:vVelocity[ 3 ];
		EntityGetVelocity( iPlayer, vVelocity );
		if ( vVelocity[0] || vVelocity[1] || vVelocity[2] )
		{
			if ( bShowMessage ) client_print( iPlayer, print_center, "Unable to %s while moving", szAction );
			return true;
		}
	}

	return false;
}

PM_FUNC_NORM_ATTRIB SkillEntity( Int:iPlayer, Int:iSkipEnt = -1 )
{
	pokemod_funcstart_debug( "SkillEntity" );

	new Int:iEnt = iSkipEnt;
	while ( ( iEnt = fm_find_ent_by_class( iEnt, szEntityPokeSkill ) ) )
	{
		if ( EntityGetNum( iEnt, ENTITY_OWNER ) == iPlayer && POKESKILL:EntityGetNum( iEnt, ENTITY_SKILL ) == g_iSkillID )
			break;
	}

	pokemod_funcend_debug( "SkillEntity" );

	return iEnt;
}
